#include "cmsis_os2.h"
#include "los_task.h"
#include "los_mux.h"
#include "lz_hardware.h"
#include "iot.h"

#include "cmsis_os2.h"
#include "ohos_init.h"

#include "MQTTClient.h"

#include "cJSON.h"

#define MQTT_DEVICES_PWD                    "12345678"

#define HOST_ADDR                           "121.36.42.100"

#define DEVICE_ID                           "smarthome_20240403"

#define PUBLISH_TOPIC                       "$oc/devices/"DEVICE_ID"/sys/properties/report"
#define SUBCRIB_TOPIC                       "$oc/devices/"DEVICE_ID"/sys/commands/#" ///request_id={request_id}"
#define RESPONSE_TOPIC                      "$oc/devices/"DEVICE_ID"/sys/commands/response" ///request_id={request_id}"

#define CLIENT_ID                           "smarthome_20240403_0_0_2024040301"
#define USERNAME                            "smarthome_20240403"
#define PASSWORD                            "cf83d9d5b73231829267bca4ffc1c11926d73d99c880a1246b6b9f51a71cf607"

#define MAX_BUFFER_LENGTH                   512
#define MAX_STRING_LENGTH                   64

static unsigned char sendBuf[MAX_BUFFER_LENGTH];
static unsigned char readBuf[MAX_BUFFER_LENGTH];

Network network;
MQTTClient client;

static unsigned int mqttConnectFlag = 0;

void send_msg_to_mqtt(void *msg, uint16_t cmd)
{
    int rc;
    MQTTMessage message;
    char *publishtopic = PUBLISH_TOPIC;
    char payload[MAX_BUFFER_LENGTH] = {0};
    char str[MAX_STRING_LENGTH] = {0};
    dev_msg_t *devMsg = (dev_msg_t *)msg;

    if (mqttConnectFlag == 0) {
        printf("mqtt not connect\n");
        return;
    }

    if (cmd != E_SMART_HOUSE_CMD_REPORT_STATE) {
        return;
    }

    cJSON *root = cJSON_CreateObject();
    if(root != NULL){
        cJSON *serv_arr = cJSON_AddArrayToObject(root, "services");
        cJSON *arr_item = cJSON_CreateObject();
        cJSON_AddStringToObject(arr_item, "service_id", "smarthome");
        cJSON *pro_obj = cJSON_CreateObject();
        cJSON_AddItemToObject(arr_item, "properties", pro_obj);

        memset(str, 0, MAX_BUFFER_LENGTH);
        sprintf(str, "%0.2f℃", (float)devMsg->smart_house.report_state.temperature/100);
        cJSON_AddStringToObject(pro_obj, "temp", str);

        memset(str, 0, MAX_BUFFER_LENGTH);
        sprintf(str, "%0.2f%%", (float)devMsg->smart_house.report_state.humidity/100);
        cJSON_AddStringToObject(pro_obj, "humi", str);
      
        memset(str, 0, MAX_BUFFER_LENGTH);
        sprintf(str, "%dlux", devMsg->smart_house.report_state.luminance);
        cJSON_AddStringToObject(pro_obj, "luminous", str);

        cJSON_AddStringToObject(pro_obj, "rain", devMsg->smart_house.report_state.rain?"false":"true");
        cJSON_AddStringToObject(pro_obj, "smoke", devMsg->smart_house.report_state.ppm?"false":"true");
        cJSON_AddStringToObject(pro_obj, "fire", devMsg->smart_house.report_state.fire?"false":"true");

        cJSON_AddItemToArray(serv_arr, arr_item);
    
        char *palyload_str = cJSON_PrintUnformatted(root);
        strcpy(payload, palyload_str);
    
        cJSON_free(palyload_str);
        cJSON_Delete(root);
    }
    
    message.qos = 0;
    message.retained = 0;
    message.payload = payload;
    message.payloadlen = strlen(payload);
    
    if ((rc = MQTTPublish(&client, publishtopic, &message)) != 0) {
        printf("Return code from MQTT publish is %d\n", rc);
        mqttConnectFlag = 0;
    } else {
        printf("mqtt publish success:%s\n", payload);
    }
}

void process_command(smart_house_cmd_type_e cmd_type, uint8_t onoff)
{
    app_msg_data_t m = {0};

    memset(&m, 0, sizeof(app_msg_data_t));
    m.dev_msg.head.type                 = E_DEV_SMART_HOUSE;
    m.dev_msg.smart_house.head.cmd_type = cmd_type;
    m.dev_msg.smart_house.on_off.onoff  = onoff;

    msg_send_to_dev(&m.dev_msg);
}

void mqtt_message_arrived(MessageData* data)
{
    int rc;
    cJSON *root = NULL;
    cJSON *cmd_name = NULL;
    char *cmd_name_str = NULL;
    cJSON *para_obj = NULL;
    cJSON *status_obj = NULL;
    char *onOff = NULL;
    char *request_id_idx=NULL;
    int request_id_len = 0;
    char request_id[50]={0};
    MQTTMessage message;
    char payload[MAX_BUFFER_LENGTH];
    char rsptopic[128]={0};

    printf("Message arrived on topic %.*s: %.*s\n", data->topicName->lenstring.len, data->topicName->lenstring.data,
           data->message->payloadlen, data->message->payload);

    //get request id
    request_id_idx = strstr(data->topicName->lenstring.data, "request_id=");
    request_id_len = data->topicName->lenstring.len - (request_id_idx - data->topicName->lenstring.data) - 11;
    //strncpy(request_id, request_id_idx+11, 19);
    strncpy(request_id, request_id_idx+11, request_id_len);
    printf("request_id = %s\n", request_id);

    //create response topic
    sprintf(rsptopic,"%s/request_id=%s",RESPONSE_TOPIC, request_id);
    printf("rsptopic = %s\n", rsptopic);

    //response message
    message.qos = 0;
    message.retained = 0;
    message.payload = payload;
    sprintf(payload, "{ \
    \"result_code\": 0, \
    \"response_name\": \"COMMAND_RESPONSE\", \
    \"paras\": { \
        \"result\": \"success\" \
    } \
    }" );
    message.payloadlen = strlen(payload);

    //publish the msg to responese topic
    if ((rc = MQTTPublish(&client, rsptopic, &message)) != 0) {
        printf("Return code from MQTT publish is %d\n", rc);
        mqttConnectFlag = 0;
    }

    /*{"command_name":"cmd","paras":{"cmd_value":"1"},"service_id":"server"}*/
    root = cJSON_ParseWithLength(data->message->payload, data->message->payloadlen);
    if (root != NULL) {
        cmd_name = cJSON_GetObjectItem(root, "command_name");
        if (cmd_name != NULL) {
            cmd_name_str = cJSON_GetStringValue(cmd_name);
            if (strcmp(cmd_name_str, "doorlock") == 0) {
                para_obj = cJSON_GetObjectItem(root, "paras");
                status_obj = cJSON_GetObjectItem(para_obj, "door");
                if (status_obj != NULL) {
                    onOff = cJSON_GetStringValue(status_obj);
                    if (strcmp(onOff, "true") == 0){
                        process_command(E_SMART_HOUSE_CMD_OPEN_DOOR, SMART_HOUSE_OPEN);
                    } else {
                        process_command(E_SMART_HOUSE_CMD_OPEN_DOOR, SMART_HOUSE_CLOSE);
                    }
                }
            } else if (strcmp(cmd_name_str, "garagelock") == 0) {
                para_obj = cJSON_GetObjectItem(root, "paras");
                status_obj = cJSON_GetObjectItem(para_obj, "garage");
                if (status_obj != NULL) {
                    onOff = cJSON_GetStringValue(status_obj);
                    if (strcmp(onOff, "true") == 0){
                        process_command(E_SMART_HOUSE_CMD_OPEN_CAR_DOOR, SMART_HOUSE_OPEN);
                    } else {
                        process_command(E_SMART_HOUSE_CMD_OPEN_CAR_DOOR, SMART_HOUSE_CLOSE);
                    }
                }
            } else if (strcmp(cmd_name_str, "windowlock") == 0) {
                para_obj = cJSON_GetObjectItem(root, "paras");
                status_obj = cJSON_GetObjectItem(para_obj, "window");
                if (status_obj != NULL) {
                    onOff = cJSON_GetStringValue(status_obj);
                    if (strcmp(onOff, "true") == 0){
                        process_command(E_SMART_HOUSE_CMD_OPEN_WINDOWS, SMART_HOUSE_OPEN);
                    } else {
                        process_command(E_SMART_HOUSE_CMD_OPEN_WINDOWS, SMART_HOUSE_CLOSE);
                    }
                }
            } else if (strcmp(cmd_name_str, "lamplock") == 0) {
                para_obj = cJSON_GetObjectItem(root, "paras");
                status_obj = cJSON_GetObjectItem(para_obj, "lamp");
                if (status_obj != NULL) {
                    onOff = cJSON_GetStringValue(status_obj);
                    if (strcmp(onOff, "true") == 0){
                        process_command(E_SMART_HOUSE_CMD_OPEN_LAMP, SMART_HOUSE_OPEN);
                    } else {
                        process_command(E_SMART_HOUSE_CMD_OPEN_LAMP, SMART_HOUSE_CLOSE);
                    }
                }
            } else if (strcmp(cmd_name_str, "devicereset") == 0) {
                para_obj = cJSON_GetObjectItem(root, "paras");
                status_obj = cJSON_GetObjectItem(para_obj, "reset");
                if (status_obj != NULL) {
                    onOff = cJSON_GetStringValue(status_obj);
                    if (strcmp(onOff, "true") == 0){
                        process_command(E_SMART_HOUSE_CMD_RESET, SMART_HOUSE_OPEN);
                    }
                }
            }
        }
    }

    cJSON_Delete(root);   
}

void mqtt_init()
{
    int rc;

    printf("Starting MQTT...\n");
    /*网络初始化*/
    NetworkInit(&network);

begin:
    /* 连接网络*/
    printf("NetworkConnect  ...\n");
    NetworkConnect(&network, HOST_ADDR, 1883);
    printf("MQTTClientInit  ...\n");
    /*MQTT客户端初始化*/
    MQTTClientInit(&client, &network, 2000, sendBuf, sizeof(sendBuf), readBuf, sizeof(readBuf));

    MQTTString clientId = MQTTString_initializer;
    clientId.cstring = CLIENT_ID;

    MQTTString userName = MQTTString_initializer;
    userName.cstring = USERNAME;

    MQTTString password = MQTTString_initializer;
    password.cstring = PASSWORD;

    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
    data.clientID = clientId;
    data.username = userName;
    data.password = password;
    data.willFlag = 0;
    data.MQTTVersion = 4;
    data.keepAliveInterval = 60;
    data.cleansession = 1;

    printf("MQTTConnect  ...\n");
    rc = MQTTConnect(&client, &data);
    if (rc != 0) {
        printf("MQTTConnect: %d\n", rc);
        NetworkDisconnect(&network);
        MQTTDisconnect(&client);
        osDelay(200);
        goto begin;
    }

    printf("MQTTSubscribe  ...\n");
    rc = MQTTSubscribe(&client, SUBCRIB_TOPIC, 0, mqtt_message_arrived);
    if (rc != 0) {
        printf("MQTTSubscribe: %d\n", rc);
        osDelay(200);
        goto begin;
    }
    mqttConnectFlag = 1;
}

//发消息到设备
void msg_send_to_dev(void *msg)
{
    dev_msg_t *m = (dev_msg_t*)msg;

    if (0 != LOS_QueueWriteCopy(g_netrev_queue, m, sizeof(dev_msg_t), QUEUE_DEFAULT_WRITE_TIME_MS))
    {
        WRITE_QUEUE_ERR_LOG(g_netrev_queue);
    }  
}

void iot_process(void *args)
{
reconnect:
    SetWifiModeOff();  
    SetWifiModeOn();
    mqtt_init();

    while (1) {
        MQTTYield(&client, 5000);
        if (mqttConnectFlag == 0) {
            goto reconnect;
        }
    }
}

